package com.roy.spring.ioc.iocHelper;

import com.roy.spring.ioc.annotation.Component;
import com.roy.spring.ioc.annotation.Inject;
import com.roy.spring.ioc.beanfactory.AnnotationContext;
import com.roy.spring.ioc.config.SpringConfig;
import com.roy.spring.ioc.util.ClassUtil;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.lang.reflect.Field;
import java.net.URL;
import java.util.Map;

/**
 * Created by mc on 18/4/23.
 */
public class IocHelperImpl extends AnnotationContext implements IocHelper {

    private static final Logger logger = LoggerFactory
            .getLogger(IocHelperImpl.class);

    public IocHelperImpl(SpringConfig config) {
        super(config);
    }

    public void init(String scanPath) {
        ClassLoader cl = ClassUtil.getClassLoader();
            String packageFileName = scanPath.replace(".", "/");
            URL urls = cl.getResource(packageFileName);

            if (urls == null) {
                throw new RuntimeException("没有找到这个包，请重新检查包名"
                        + scanPath + "是正确");}

            String protocol = urls.getProtocol();
            if ("file".equals(protocol)) {
                File file = new File(urls.toString().substring(5));

                loadClass(file, scanPath);
            }

    }


    public <T> void inject() {
        for (Map.Entry<String, Object> entry : container.entrySet()) {
            T t = (T) entry.getValue();
            for (Field f : t.getClass().getDeclaredFields()) {
                Inject inject = f.getAnnotation(Inject.class);
                if (inject != null) {
                    try {
                        f.setAccessible(true);
                        Object obj = getInjectedBean(inject, f);
                        if (obj == null) {
                            logger.info("依赖对象为空");
                            break;
                        }
                        f.set(t, obj);
                    } catch (Exception e) {
                        throw new RuntimeException("注入依赖属性失败", e);
                    }
                }
            }
        }
    }

    public void register(String basePath) {
        {
            if (basePath == null) {
                throw new NullPointerException("basePath不能为空");
            }

            // 将扫描到的类文件加载到spring的存储容器map中
            init(basePath);

            // 然后将所有依赖的属性一一注入
            inject();
        }
    }


    private <T> void loadClass(File file, String packageName) {
        File[] children = file.listFiles();

        for (File child : children) {
            if (child.isFile()) {
                String name = child.getName();
                if (name.endsWith(".class")) {
                    try {
                        Class<? extends T> targetClass = (Class<? extends T>) Class
                                .forName(packageName + "."
                                        + name.substring(0, name.length() - 6));
                        loadBean(targetClass);
                    } catch (ClassNotFoundException e) {
                        throw new RuntimeException("包名" + packageName
                                + "填写错误导致类文件加载出现异常");
                    }
                }
            } else if (child.isDirectory()) {
                loadClass(child, packageName + "." + child.getName());
            }
        }
    }

    private <T> void loadBean(Class<? extends T> targetClass) {
        Component com = targetClass.getAnnotation(Component.class);
        if (com != null) {
            try {
                String key = null;
                T bean = targetClass.newInstance();
                if (!StringUtils.isBlank(com.value())) {
                    key = com.value();
                } else {
                    key = StringUtils.uncapitalize(targetClass.getSimpleName());
                }
                container.put(key, bean);

            } catch (Exception e) {
                throw new RuntimeException("无法访问" + targetClass.getName()
                        + "的空构造方法");
            }
        }
    }

    private Object getInjectedBean(Inject inject, Field f) {
        // 按照以下优先级注入：@inject注解上配置的name->@inject注解上配置的class->属性变量名
        if (!StringUtils.isBlank(inject.name())) {
            return getBean(inject.name());
        }

        Object obj = getBean(inject.type());

        if (obj != null) {
            return obj;
        }

        return getBean(f.getName());

    }

}
